/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"jit/BaselineCacheIRCompiler.h"#include"jit/CacheIR.h"#include"jit/Linker.h"#include"jit/SharedICHelpers.h"#include"proxy/Proxy.h"#include"jscntxtinlines.h"#include"jscompartmentinlines.h"#include"jit/MacroAssembler-inl.h"usingnamespacejs;usingnamespacejs::jit;usingmozilla::Maybe;classAutoStubFrame;AddressCacheRegisterAllocator::addressOf(MacroAssembler&masm,BaselineFrameSlotslot)const{uint32_toffset=stackPushed_+ICStackValueOffset+slot.slot()*sizeof(JS::Value);returnAddress(masm.getStackPointer(),offset);}// BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.classMOZ_RAIIBaselineCacheIRCompiler:publicCacheIRCompiler{#ifdef DEBUG// Some Baseline IC stubs can be used in IonMonkey through SharedStubs.// Those stubs have different machine code, so we need to track whether// we're compiling for Baseline or Ion.ICStubEngineengine_;#endifuint32_tstubDataOffset_;boolinStubFrame_;boolmakesGCCalls_;MOZ_MUST_USEboolcallVM(MacroAssembler&masm,constVMFunction&fun);MOZ_MUST_USEboolcallTypeUpdateIC(Registerobj,ValueOperandval,Registerscratch,LiveGeneralRegisterSetsaveRegs);MOZ_MUST_USEboolemitStoreSlotShared(boolisFixed);MOZ_MUST_USEboolemitAddAndStoreSlotShared(CacheOpop);public:friendclassAutoStubFrame;BaselineCacheIRCompiler(JSContext*cx,constCacheIRWriter&writer,ICStubEngineengine,uint32_tstubDataOffset):CacheIRCompiler(cx,writer,Mode::Baseline),#ifdef DEBUGengine_(engine),#endifstubDataOffset_(stubDataOffset),inStubFrame_(false),makesGCCalls_(false){}MOZ_MUST_USEboolinit(CacheKindkind);JitCode*compile();boolmakesGCCalls()const{returnmakesGCCalls_;}private:#define DEFINE_OP(op) MOZ_MUST_USE bool emit##op();CACHE_IR_OPS(DEFINE_OP)#undef DEFINE_OPAddressstubAddress(uint32_toffset)const{returnAddress(ICStubReg,stubDataOffset_+offset);}};#define DEFINE_SHARED_OP(op) \ bool BaselineCacheIRCompiler::emit##op() { return CacheIRCompiler::emit##op(); }CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)#undef DEFINE_SHARED_OPenumclassCallCanGC{CanGC,CanNotGC};// Instructions that have to perform a callVM require a stub frame. Call its// enter() and leave() methods to enter/leave the stub frame.classMOZ_RAIIAutoStubFrame{BaselineCacheIRCompiler&compiler;#ifdef DEBUGuint32_tframePushedAtEnterStubFrame_;#endifAutoStubFrame(constAutoStubFrame&)=delete;voidoperator=(constAutoStubFrame&)=delete;public:explicitAutoStubFrame(BaselineCacheIRCompiler&compiler):compiler(compiler)#ifdef DEBUG,framePushedAtEnterStubFrame_(0)#endif{}voidenter(MacroAssembler&masm,Registerscratch,CallCanGCcanGC=CallCanGC::CanGC){MOZ_ASSERT(compiler.allocator.stackPushed()==0);MOZ_ASSERT(compiler.engine_==ICStubEngine::Baseline);EmitBaselineEnterStubFrame(masm,scratch);#ifdef DEBUGframePushedAtEnterStubFrame_=masm.framePushed();#endifMOZ_ASSERT(!compiler.inStubFrame_);compiler.inStubFrame_=true;if(canGC==CallCanGC::CanGC)compiler.makesGCCalls_=true;}voidleave(MacroAssembler&masm,boolcalledIntoIon=false){MOZ_ASSERT(compiler.inStubFrame_);compiler.inStubFrame_=false;#ifdef DEBUGmasm.setFramePushed(framePushedAtEnterStubFrame_);if(calledIntoIon)masm.adjustFrame(sizeof(intptr_t));// Calls into ion have this extra.#endifEmitBaselineLeaveStubFrame(masm,calledIntoIon);}#ifdef DEBUG~AutoStubFrame(){MOZ_ASSERT(!compiler.inStubFrame_);}#endif};boolBaselineCacheIRCompiler::callVM(MacroAssembler&masm,constVMFunction&fun){MOZ_ASSERT(inStubFrame_);JitCode*code=cx_->runtime()->jitRuntime()->getVMWrapper(fun);if(!code)returnfalse;MOZ_ASSERT(fun.expectTailCall==NonTailCall);MOZ_ASSERT(engine_==ICStubEngine::Baseline);EmitBaselineCallVM(code,masm);returntrue;}JitCode*BaselineCacheIRCompiler::compile(){#ifndef JS_USE_LINK_REGISTER// The first value contains the return addres,// which we pull into ICTailCallReg for tail calls.masm.adjustFrame(sizeof(intptr_t));#endif#ifdef JS_CODEGEN_ARMmasm.setSecondScratchReg(BaselineSecondScratchReg);#endifdo{switch(reader.readOp()){#define DEFINE_OP(op) \ case CacheOp::op: \ if (!emit##op()) \ return nullptr; \ break;CACHE_IR_OPS(DEFINE_OP)#undef DEFINE_OPdefault:MOZ_CRASH("Invalid op");}allocator.nextOp();}while(reader.more());MOZ_ASSERT(!inStubFrame_);masm.assumeUnreachable("Should have returned from IC");// Done emitting the main IC code. Now emit the failure paths.for(size_ti=0;i<failurePaths.length();i++){if(!emitFailurePath(i))returnnullptr;EmitStubGuardFailure(masm);}Linkerlinker(masm);AutoFlushICacheafc("getStubCode");Rooted<JitCode*>newStubCode(cx_,linker.newCode<NoGC>(cx_,BASELINE_CODE));if(!newStubCode){cx_->recoverFromOutOfMemory();returnnullptr;}returnnewStubCode;}boolBaselineCacheIRCompiler::emitGuardShape(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterscratch(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.loadPtr(addr,scratch);masm.branchTestObjShape(Assembler::NotEqual,obj,scratch,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardGroup(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterscratch(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.loadPtr(addr,scratch);masm.branchTestObjGroup(Assembler::NotEqual,obj,scratch,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardGroupHasUnanalyzedNewScript(){Addressaddr(stubAddress(reader.stubOffset()));AutoScratchRegisterscratch1(allocator,masm);AutoScratchRegisterscratch2(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.loadPtr(addr,scratch1);masm.guardGroupHasUnanalyzedNewScript(scratch1,scratch2,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardProto(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterscratch(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.loadObjProto(obj,scratch);masm.branchPtr(Assembler::NotEqual,addr,scratch,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardCompartment(){Registerobj=allocator.useRegister(masm,reader.objOperandId());reader.stubOffset();// Read global wrapper.AutoScratchRegisterscratch(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.loadPtr(Address(obj,JSObject::offsetOfGroup()),scratch);masm.loadPtr(Address(scratch,ObjectGroup::offsetOfCompartment()),scratch);masm.branchPtr(Assembler::NotEqual,addr,scratch,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardSpecificObject(){Registerobj=allocator.useRegister(masm,reader.objOperandId());FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.branchPtr(Assembler::NotEqual,addr,obj,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardSpecificAtom(){Registerstr=allocator.useRegister(masm,reader.stringOperandId());AutoScratchRegisterscratch(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;AddressatomAddr(stubAddress(reader.stubOffset()));Labeldone;masm.branchPtr(Assembler::Equal,atomAddr,str,&done);// The pointers are not equal, so if the input string is also an atom it// must be a different string.masm.branchTest32(Assembler::NonZero,Address(str,JSString::offsetOfFlags()),Imm32(JSString::ATOM_BIT),failure->label());// Check the length.masm.loadPtr(atomAddr,scratch);masm.loadStringLength(scratch,scratch);masm.branch32(Assembler::NotEqual,Address(str,JSString::offsetOfLength()),scratch,failure->label());// We have a non-atomized string with the same length. Call a helper// function to do the comparison.LiveRegisterSetvolatileRegs(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());masm.PushRegsInMask(volatileRegs);masm.setupUnalignedABICall(scratch);masm.loadPtr(atomAddr,scratch);masm.passABIArg(scratch);masm.passABIArg(str);masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,EqualStringsHelper));masm.mov(ReturnReg,scratch);LiveRegisterSetignore;ignore.add(scratch);masm.PopRegsInMaskIgnore(volatileRegs,ignore);masm.branchIfFalseBool(scratch,failure->label());masm.bind(&done);returntrue;}boolBaselineCacheIRCompiler::emitGuardSpecificSymbol(){Registersym=allocator.useRegister(masm,reader.symbolOperandId());FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Addressaddr(stubAddress(reader.stubOffset()));masm.branchPtr(Assembler::NotEqual,addr,sym,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitLoadFixedSlotResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);masm.load32(stubAddress(reader.stubOffset()),scratch);masm.loadValue(BaseIndex(obj,scratch,TimesOne),output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadDynamicSlotResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);AutoScratchRegisterscratch2(allocator,masm);masm.load32(stubAddress(reader.stubOffset()),scratch);masm.loadPtr(Address(obj,NativeObject::offsetOfSlots()),scratch2);masm.loadValue(BaseIndex(scratch2,scratch,TimesOne),output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitMegamorphicLoadSlotResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressnameAddr=stubAddress(reader.stubOffset());boolhandleMissing=reader.readBool();AutoScratchRegisterMaybeOutputscratch1(allocator,masm,output);AutoScratchRegisterscratch2(allocator,masm);AutoScratchRegisterscratch3(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.Push(UndefinedValue());masm.moveStackPtrTo(scratch3.get());LiveRegisterSetvolatileRegs(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());volatileRegs.takeUnchecked(scratch1);volatileRegs.takeUnchecked(scratch2);volatileRegs.takeUnchecked(scratch3);masm.PushRegsInMask(volatileRegs);masm.setupUnalignedABICall(scratch1);masm.loadJSContext(scratch1);masm.passABIArg(scratch1);masm.passABIArg(obj);masm.loadPtr(nameAddr,scratch2);masm.passABIArg(scratch2);masm.passABIArg(scratch3);if(handleMissing)masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,(GetNativeDataProperty<true>)));elsemasm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,(GetNativeDataProperty<false>)));masm.mov(ReturnReg,scratch2);masm.PopRegsInMask(volatileRegs);masm.loadTypedOrValue(Address(masm.getStackPointer(),0),output);masm.adjustStack(sizeof(Value));masm.branchIfFalseBool(scratch2,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitMegamorphicStoreSlot(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressnameAddr=stubAddress(reader.stubOffset());ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());boolneedsTypeBarrier=reader.readBool();AutoScratchRegisterscratch1(allocator,masm);AutoScratchRegisterscratch2(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.Push(val);masm.moveStackPtrTo(val.scratchReg());LiveRegisterSetvolatileRegs(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());volatileRegs.takeUnchecked(scratch1);volatileRegs.takeUnchecked(scratch2);volatileRegs.takeUnchecked(val);masm.PushRegsInMask(volatileRegs);masm.setupUnalignedABICall(scratch1);masm.loadJSContext(scratch1);masm.passABIArg(scratch1);masm.passABIArg(obj);masm.loadPtr(nameAddr,scratch2);masm.passABIArg(scratch2);masm.passABIArg(val.scratchReg());if(needsTypeBarrier)masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,(SetNativeDataProperty<true>)));elsemasm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,(SetNativeDataProperty<false>)));masm.mov(ReturnReg,scratch1);masm.PopRegsInMask(volatileRegs);masm.loadValue(Address(masm.getStackPointer(),0),val);masm.adjustStack(sizeof(Value));masm.branchIfFalseBool(scratch1,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitGuardHasGetterSetter(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressshapeAddr=stubAddress(reader.stubOffset());AutoScratchRegisterscratch1(allocator,masm);AutoScratchRegisterscratch2(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;LiveRegisterSetvolatileRegs(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());volatileRegs.takeUnchecked(scratch1);volatileRegs.takeUnchecked(scratch2);masm.PushRegsInMask(volatileRegs);masm.setupUnalignedABICall(scratch1);masm.loadJSContext(scratch1);masm.passABIArg(scratch1);masm.passABIArg(obj);masm.loadPtr(shapeAddr,scratch2);masm.passABIArg(scratch2);masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,ObjectHasGetterSetter));masm.mov(ReturnReg,scratch1);masm.PopRegsInMask(volatileRegs);masm.branchIfFalseBool(scratch1,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitCallScriptedGetterResult(){MOZ_ASSERT(engine_==ICStubEngine::Baseline);Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressgetterAddr(stubAddress(reader.stubOffset()));AutoScratchRegisterExcludingcode(allocator,masm,ArgumentsRectifierReg);AutoScratchRegistercallee(allocator,masm);AutoScratchRegisterscratch(allocator,masm);// First, ensure our getter is non-lazy and has JIT code.{FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.loadPtr(getterAddr,callee);masm.branchIfFunctionHasNoScript(callee,failure->label());masm.loadPtr(Address(callee,JSFunction::offsetOfNativeOrScript()),code);masm.loadBaselineOrIonRaw(code,code,failure->label());}allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Align the stack such that the JitFrameLayout is aligned on// JitStackAlignment.masm.alignJitStackBasedOnNArgs(0);// Getter is called with 0 arguments, just |obj| as thisv.// Note that we use Push, not push, so that callJit will align the stack// properly on ARM.masm.Push(TypedOrValueRegister(MIRType::Object,AnyRegister(obj)));EmitBaselineCreateStubFrameDescriptor(masm,scratch,JitFrameLayout::Size());masm.Push(Imm32(0));// ActualArgc is 0masm.Push(callee);masm.Push(scratch);// Handle arguments underflow.LabelnoUnderflow;masm.load16ZeroExtend(Address(callee,JSFunction::offsetOfNargs()),callee);masm.branch32(Assembler::Equal,callee,Imm32(0),&noUnderflow);{// Call the arguments rectifier.MOZ_ASSERT(ArgumentsRectifierReg!=code);JitCode*argumentsRectifier=cx_->runtime()->jitRuntime()->getArgumentsRectifier();masm.movePtr(ImmGCPtr(argumentsRectifier),code);masm.loadPtr(Address(code,JitCode::offsetOfCode()),code);masm.movePtr(ImmWord(0),ArgumentsRectifierReg);}masm.bind(&noUnderflow);masm.callJit(code);stubFrame.leave(masm,true);returntrue;}typedefbool(*CallNativeGetterFn)(JSContext*,HandleFunction,HandleObject,MutableHandleValue);staticconstVMFunctionCallNativeGetterInfo=FunctionInfo<CallNativeGetterFn>(CallNativeGetter,"CallNativeGetter");boolBaselineCacheIRCompiler::emitCallNativeGetterResult(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressgetterAddr(stubAddress(reader.stubOffset()));AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Load the callee in the scratch register.masm.loadPtr(getterAddr,scratch);masm.Push(obj);masm.Push(scratch);if(!callVM(masm,CallNativeGetterInfo))returnfalse;stubFrame.leave(masm);returntrue;}typedefbool(*ProxyGetPropertyFn)(JSContext*,HandleObject,HandleId,MutableHandleValue);staticconstVMFunctionProxyGetPropertyInfo=FunctionInfo<ProxyGetPropertyFn>(ProxyGetProperty,"ProxyGetProperty");boolBaselineCacheIRCompiler::emitCallProxyGetResult(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressidAddr(stubAddress(reader.stubOffset()));AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Load the jsid in the scratch register.masm.loadPtr(idAddr,scratch);masm.Push(scratch);masm.Push(obj);if(!callVM(masm,ProxyGetPropertyInfo))returnfalse;stubFrame.leave(masm);returntrue;}typedefbool(*ProxyGetPropertyByValueFn)(JSContext*,HandleObject,HandleValue,MutableHandleValue);staticconstVMFunctionProxyGetPropertyByValueInfo=FunctionInfo<ProxyGetPropertyByValueFn>(ProxyGetPropertyByValue,"ProxyGetPropertyByValue");boolBaselineCacheIRCompiler::emitCallProxyGetByValueResult(){Registerobj=allocator.useRegister(masm,reader.objOperandId());ValueOperandidVal=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);masm.Push(idVal);masm.Push(obj);if(!callVM(masm,ProxyGetPropertyByValueInfo))returnfalse;stubFrame.leave(masm);returntrue;}typedefbool(*ProxyHasOwnFn)(JSContext*,HandleObject,HandleValue,MutableHandleValue);staticconstVMFunctionProxyHasOwnInfo=FunctionInfo<ProxyHasOwnFn>(ProxyHasOwn,"ProxyHasOwn");boolBaselineCacheIRCompiler::emitCallProxyHasOwnResult(){Registerobj=allocator.useRegister(masm,reader.objOperandId());ValueOperandidVal=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);masm.Push(idVal);masm.Push(obj);if(!callVM(masm,ProxyHasOwnInfo))returnfalse;stubFrame.leave(masm);returntrue;}boolBaselineCacheIRCompiler::emitLoadUnboxedPropertyResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);JSValueTypefieldType=reader.valueType();AddressfieldOffset(stubAddress(reader.stubOffset()));masm.load32(fieldOffset,scratch);masm.loadUnboxedProperty(BaseIndex(obj,scratch,TimesOne),fieldType,output);returntrue;}boolBaselineCacheIRCompiler::emitGuardFrameHasNoArgumentsObject(){FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.branchTest32(Assembler::NonZero,Address(BaselineFrameReg,BaselineFrame::reverseOffsetOfFlags()),Imm32(BaselineFrame::HAS_ARGS_OBJ),failure->label());returntrue;}boolBaselineCacheIRCompiler::emitLoadFrameCalleeResult(){AutoOutputRegisteroutput(*this);AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);Addresscallee(BaselineFrameReg,BaselineFrame::offsetOfCalleeToken());masm.loadFunctionFromCalleeToken(callee,scratch);masm.tagValue(JSVAL_TYPE_OBJECT,scratch,output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadFrameNumActualArgsResult(){AutoOutputRegisteroutput(*this);AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);AddressactualArgs(BaselineFrameReg,BaselineFrame::offsetOfNumActualArgs());masm.loadPtr(actualArgs,scratch);masm.tagValue(JSVAL_TYPE_INT32,scratch,output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadTypedObjectResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterscratch1(allocator,masm);AutoScratchRegisterscratch2(allocator,masm);TypedThingLayoutlayout=reader.typedThingLayout();uint32_ttypeDescr=reader.typeDescrKey();AddressfieldOffset(stubAddress(reader.stubOffset()));// Get the object's data pointer.LoadTypedThingData(masm,layout,obj,scratch1);// Get the address being written to.masm.load32(fieldOffset,scratch2);masm.addPtr(scratch2,scratch1);AddressfieldAddr(scratch1,0);emitLoadTypedObjectResultShared(fieldAddr,scratch2,layout,typeDescr,output);returntrue;}boolBaselineCacheIRCompiler::emitLoadFrameArgumentResult(){AutoOutputRegisteroutput(*this);Registerindex=allocator.useRegister(masm,reader.int32OperandId());AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Bounds check.masm.loadPtr(Address(BaselineFrameReg,BaselineFrame::offsetOfNumActualArgs()),scratch);masm.branch32(Assembler::AboveOrEqual,index,scratch,failure->label());// Load the argument.masm.loadValue(BaseValueIndex(BaselineFrameReg,index,BaselineFrame::offsetOfArg(0)),output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadEnvironmentFixedSlotResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.load32(stubAddress(reader.stubOffset()),scratch);BaseIndexslot(obj,scratch,TimesOne);// Check for uninitialized lexicals.masm.branchTestMagic(Assembler::Equal,slot,failure->label());// Load the value.masm.loadValue(slot,output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadEnvironmentDynamicSlotResult(){AutoOutputRegisteroutput(*this);Registerobj=allocator.useRegister(masm,reader.objOperandId());AutoScratchRegisterscratch(allocator,masm);AutoScratchRegisterMaybeOutputscratch2(allocator,masm,output);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.load32(stubAddress(reader.stubOffset()),scratch);masm.loadPtr(Address(obj,NativeObject::offsetOfSlots()),scratch2);// Check for uninitialized lexicals.BaseIndexslot(scratch2,scratch,TimesOne);masm.branchTestMagic(Assembler::Equal,slot,failure->label());// Load the value.masm.loadValue(slot,output.valueReg());returntrue;}boolBaselineCacheIRCompiler::emitLoadStringResult(){AutoOutputRegisteroutput(*this);AutoScratchRegisterMaybeOutputscratch(allocator,masm,output);masm.loadPtr(stubAddress(reader.stubOffset()),scratch);masm.tagValue(JSVAL_TYPE_STRING,scratch,output.valueReg());returntrue;}typedefbool(*StringSplitHelperFn)(JSContext*,HandleString,HandleString,HandleObjectGroup,uint32_tlimit,MutableHandleValue);staticconstVMFunctionStringSplitHelperInfo=FunctionInfo<StringSplitHelperFn>(StringSplitHelper,"StringSplitHelper");boolBaselineCacheIRCompiler::emitCallStringSplitResult(){Registerstr=allocator.useRegister(masm,reader.stringOperandId());Registersep=allocator.useRegister(masm,reader.stringOperandId());AddressgroupAddr(stubAddress(reader.stubOffset()));AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Load the group in the scratch register.masm.loadPtr(groupAddr,scratch);masm.Push(Imm32(INT32_MAX));masm.Push(scratch);masm.Push(sep);masm.Push(str);if(!callVM(masm,StringSplitHelperInfo))returnfalse;stubFrame.leave(masm);returntrue;}boolBaselineCacheIRCompiler::callTypeUpdateIC(Registerobj,ValueOperandval,Registerscratch,LiveGeneralRegisterSetsaveRegs){// Ensure the stack is empty for the VM call below.allocator.discardStack(masm);// R0 contains the value that needs to be typechecked.MOZ_ASSERT(val==R0);MOZ_ASSERT(scratch==R1.scratchReg());#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)staticconstboolCallClobbersTailReg=false;#elsestaticconstboolCallClobbersTailReg=true;#endif// Call the first type update stub.if(CallClobbersTailReg)masm.push(ICTailCallReg);masm.push(ICStubReg);masm.loadPtr(Address(ICStubReg,ICUpdatedStub::offsetOfFirstUpdateStub()),ICStubReg);masm.call(Address(ICStubReg,ICStub::offsetOfStubCode()));masm.pop(ICStubReg);if(CallClobbersTailReg)masm.pop(ICTailCallReg);// The update IC will store 0 or 1 in |scratch|, R1.scratchReg(), reflecting// if the value in R0 type-checked properly or not.Labeldone;masm.branch32(Assembler::Equal,scratch,Imm32(1),&done);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch,CallCanGC::CanNotGC);masm.PushRegsInMask(saveRegs);masm.Push(val);masm.Push(TypedOrValueRegister(MIRType::Object,AnyRegister(obj)));masm.Push(ICStubReg);// Load previous frame pointer, push BaselineFrame*.masm.loadPtr(Address(BaselineFrameReg,0),scratch);masm.pushBaselineFramePtr(scratch,scratch);if(!callVM(masm,DoTypeUpdateFallbackInfo))returnfalse;masm.PopRegsInMask(saveRegs);stubFrame.leave(masm);masm.bind(&done);returntrue;}boolBaselineCacheIRCompiler::emitStoreSlotShared(boolisFixed){ObjOperandIdobjId=reader.objOperandId();AddressoffsetAddr=stubAddress(reader.stubOffset());// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch1(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);Maybe<AutoScratchRegister>scratch2;if(!isFixed)scratch2.emplace(allocator,masm);LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch1,saveRegs))returnfalse;masm.load32(offsetAddr,scratch1);if(isFixed){BaseIndexslot(obj,scratch1,TimesOne);EmitPreBarrier(masm,slot,MIRType::Value);masm.storeValue(val,slot);}else{masm.loadPtr(Address(obj,NativeObject::offsetOfSlots()),scratch2.ref());BaseIndexslot(scratch2.ref(),scratch1,TimesOne);EmitPreBarrier(masm,slot,MIRType::Value);masm.storeValue(val,slot);}emitPostBarrierSlot(obj,val,scratch1);returntrue;}boolBaselineCacheIRCompiler::emitStoreFixedSlot(){returnemitStoreSlotShared(true);}boolBaselineCacheIRCompiler::emitStoreDynamicSlot(){returnemitStoreSlotShared(false);}boolBaselineCacheIRCompiler::emitAddAndStoreSlotShared(CacheOpop){ObjOperandIdobjId=reader.objOperandId();AddressoffsetAddr=stubAddress(reader.stubOffset());// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch1(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);AutoScratchRegisterscratch2(allocator,masm);boolchangeGroup=reader.readBool();AddressnewGroupAddr=stubAddress(reader.stubOffset());AddressnewShapeAddr=stubAddress(reader.stubOffset());if(op==CacheOp::AllocateAndStoreDynamicSlot){// We have to (re)allocate dynamic slots. Do this first, as it's the// only fallible operation here. This simplifies the callTypeUpdateIC// call below: it does not have to worry about saving registers used by// failure paths. Note that growSlotsDontReportOOM is fallible but does// not GC.AddressnumNewSlotsAddr=stubAddress(reader.stubOffset());FailurePath*failure;if(!addFailurePath(&failure))returnfalse;LiveRegisterSetsave(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());masm.PushRegsInMask(save);masm.setupUnalignedABICall(scratch1);masm.loadJSContext(scratch1);masm.passABIArg(scratch1);masm.passABIArg(obj);masm.load32(numNewSlotsAddr,scratch2);masm.passABIArg(scratch2);masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,NativeObject::growSlotsDontReportOOM));masm.mov(ReturnReg,scratch1);LiveRegisterSetignore;ignore.add(scratch1);masm.PopRegsInMaskIgnore(save,ignore);masm.branchIfFalseBool(scratch1,failure->label());}LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch1,saveRegs))returnfalse;if(changeGroup){// Changing object's group from a partially to fully initialized group,// per the acquired properties analysis. Only change the group if the// old group still has a newScript. This only applies to PlainObjects.LabelnoGroupChange;masm.loadPtr(Address(obj,JSObject::offsetOfGroup()),scratch1);masm.branchPtr(Assembler::Equal,Address(scratch1,ObjectGroup::offsetOfAddendum()),ImmWord(0),&noGroupChange);// Reload the new group from the cache.masm.loadPtr(newGroupAddr,scratch1);AddressgroupAddr(obj,JSObject::offsetOfGroup());EmitPreBarrier(masm,groupAddr,MIRType::ObjectGroup);masm.storePtr(scratch1,groupAddr);masm.bind(&noGroupChange);}// Update the object's shape.AddressshapeAddr(obj,ShapedObject::offsetOfShape());masm.loadPtr(newShapeAddr,scratch1);EmitPreBarrier(masm,shapeAddr,MIRType::Shape);masm.storePtr(scratch1,shapeAddr);// Perform the store. No pre-barrier required since this is a new// initialization.masm.load32(offsetAddr,scratch1);if(op==CacheOp::AddAndStoreFixedSlot){BaseIndexslot(obj,scratch1,TimesOne);masm.storeValue(val,slot);}else{MOZ_ASSERT(op==CacheOp::AddAndStoreDynamicSlot||op==CacheOp::AllocateAndStoreDynamicSlot);masm.loadPtr(Address(obj,NativeObject::offsetOfSlots()),scratch2);BaseIndexslot(scratch2,scratch1,TimesOne);masm.storeValue(val,slot);}emitPostBarrierSlot(obj,val,scratch1);returntrue;}boolBaselineCacheIRCompiler::emitAddAndStoreFixedSlot(){returnemitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);}boolBaselineCacheIRCompiler::emitAddAndStoreDynamicSlot(){returnemitAddAndStoreSlotShared(CacheOp::AddAndStoreDynamicSlot);}boolBaselineCacheIRCompiler::emitAllocateAndStoreDynamicSlot(){returnemitAddAndStoreSlotShared(CacheOp::AllocateAndStoreDynamicSlot);}boolBaselineCacheIRCompiler::emitStoreUnboxedProperty(){ObjOperandIdobjId=reader.objOperandId();JSValueTypefieldType=reader.valueType();AddressoffsetAddr=stubAddress(reader.stubOffset());// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);// We only need the type update IC if we are storing an object.if(fieldType==JSVAL_TYPE_OBJECT){LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch,saveRegs))returnfalse;}masm.load32(offsetAddr,scratch);BaseIndexfieldAddr(obj,scratch,TimesOne);// Note that the storeUnboxedProperty call here is infallible, as the// IR emitter is responsible for guarding on |val|'s type.EmitICUnboxedPreBarrier(masm,fieldAddr,fieldType);masm.storeUnboxedProperty(fieldAddr,fieldType,ConstantOrRegister(TypedOrValueRegister(val)),/* failure = */nullptr);if(UnboxedTypeNeedsPostBarrier(fieldType))emitPostBarrierSlot(obj,val,scratch);returntrue;}boolBaselineCacheIRCompiler::emitStoreTypedObjectReferenceProperty(){ObjOperandIdobjId=reader.objOperandId();AddressoffsetAddr=stubAddress(reader.stubOffset());TypedThingLayoutlayout=reader.typedThingLayout();ReferenceTypeDescr::Typetype=reader.referenceTypeDescrType();// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch1(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);AutoScratchRegisterscratch2(allocator,masm);// We don't need a type update IC if the property is always a string.if(type!=ReferenceTypeDescr::TYPE_STRING){LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch1,saveRegs))returnfalse;}// Compute the address being written to.LoadTypedThingData(masm,layout,obj,scratch1);masm.addPtr(offsetAddr,scratch1);Addressdest(scratch1,0);emitStoreTypedObjectReferenceProp(val,type,dest,scratch2);if(type!=ReferenceTypeDescr::TYPE_STRING)emitPostBarrierSlot(obj,val,scratch1);returntrue;}boolBaselineCacheIRCompiler::emitStoreTypedObjectScalarProperty(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressoffsetAddr=stubAddress(reader.stubOffset());TypedThingLayoutlayout=reader.typedThingLayout();Scalar::Typetype=reader.scalarType();ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegisterscratch1(allocator,masm);AutoScratchRegisterscratch2(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Compute the address being written to.LoadTypedThingData(masm,layout,obj,scratch1);masm.addPtr(offsetAddr,scratch1);Addressdest(scratch1,0);StoreToTypedArray(cx_,masm,type,val,dest,scratch2,failure->label());returntrue;}boolBaselineCacheIRCompiler::emitStoreDenseElement(){ObjOperandIdobjId=reader.objOperandId();Int32OperandIdindexId=reader.int32OperandId();// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);Registerindex=allocator.useRegister(masm,indexId);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Load obj->elements in scratch.masm.loadPtr(Address(obj,NativeObject::offsetOfElements()),scratch);// Bounds check.AddressinitLength(scratch,ObjectElements::offsetOfInitializedLength());masm.branch32(Assembler::BelowOrEqual,initLength,index,failure->label());// Hole check.BaseObjectElementIndexelement(scratch,index);masm.branchTestMagic(Assembler::Equal,element,failure->label());// Perform a single test to see if we either need to convert double// elements, clone the copy on write elements in the object or fail// due to a frozen element.LabelnoSpecialHandling;AddresselementsFlags(scratch,ObjectElements::offsetOfFlags());masm.branchTest32(Assembler::Zero,elementsFlags,Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS|ObjectElements::COPY_ON_WRITE|ObjectElements::FROZEN),&noSpecialHandling);// Fail if we need to clone copy on write elements or to throw due// to a frozen element.masm.branchTest32(Assembler::NonZero,elementsFlags,Imm32(ObjectElements::COPY_ON_WRITE|ObjectElements::FROZEN),failure->label());// We need to convert int32 values being stored into doubles. Note that// double arrays are only created by IonMonkey, so if we have no FP support// Ion is disabled and there should be no double arrays.if(cx_->runtime()->jitSupportsFloatingPoint){// It's fine to convert the value in place in Baseline. We can't do// this in Ion.masm.convertInt32ValueToDouble(val);}else{masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");}masm.bind(&noSpecialHandling);// Call the type update IC. After this everything must be infallible as we// don't save all registers here.LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(index);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch,saveRegs))returnfalse;// Perform the store. Reload obj->elements because callTypeUpdateIC// used the scratch register.masm.loadPtr(Address(obj,NativeObject::offsetOfElements()),scratch);EmitPreBarrier(masm,element,MIRType::Value);masm.storeValue(val,element);emitPostBarrierElement(obj,val,scratch,index);returntrue;}boolBaselineCacheIRCompiler::emitStoreDenseElementHole(){ObjOperandIdobjId=reader.objOperandId();Int32OperandIdindexId=reader.int32OperandId();// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);Registerobj=allocator.useRegister(masm,objId);Registerindex=allocator.useRegister(masm,indexId);boolhandleAdd=reader.readBool();FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Load obj->elements in scratch.masm.loadPtr(Address(obj,NativeObject::offsetOfElements()),scratch);BaseObjectElementIndexelement(scratch,index);AddressinitLength(scratch,ObjectElements::offsetOfInitializedLength());AddresselementsFlags(scratch,ObjectElements::offsetOfFlags());// Check for copy-on-write or frozen elements.masm.branchTest32(Assembler::NonZero,elementsFlags,Imm32(ObjectElements::COPY_ON_WRITE|ObjectElements::FROZEN),failure->label());if(handleAdd){// Fail if index > initLength.masm.branch32(Assembler::Below,initLength,index,failure->label());// If index < capacity, we can add a dense element inline. If not we// need to allocate more elements.LabelcapacityOk;Addresscapacity(scratch,ObjectElements::offsetOfCapacity());masm.branch32(Assembler::Above,capacity,index,&capacityOk);// Check for non-writable array length. We only have to do this if// index >= capacity.masm.branchTest32(Assembler::NonZero,elementsFlags,Imm32(ObjectElements::NONWRITABLE_ARRAY_LENGTH),failure->label());LiveRegisterSetsave(GeneralRegisterSet::Volatile(),liveVolatileFloatRegs());save.takeUnchecked(scratch);masm.PushRegsInMask(save);masm.setupUnalignedABICall(scratch);masm.loadJSContext(scratch);masm.passABIArg(scratch);masm.passABIArg(obj);masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*,NativeObject::addDenseElementDontReportOOM));masm.mov(ReturnReg,scratch);masm.PopRegsInMask(save);masm.branchIfFalseBool(scratch,failure->label());// Load the reallocated elements pointer.masm.loadPtr(Address(obj,NativeObject::offsetOfElements()),scratch);masm.bind(&capacityOk);// We increment initLength after the callTypeUpdateIC call, to ensure// the type update code doesn't read uninitialized memory.}else{// Fail if index >= initLength.masm.branch32(Assembler::BelowOrEqual,initLength,index,failure->label());}// Check if we have to convert a double element.LabelnoConversion;masm.branchTest32(Assembler::Zero,elementsFlags,Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),&noConversion);// We need to convert int32 values being stored into doubles. Note that// double arrays are only created by IonMonkey, so if we have no FP support// Ion is disabled and there should be no double arrays.if(cx_->runtime()->jitSupportsFloatingPoint){// It's fine to convert the value in place in Baseline. We can't do// this in Ion.masm.convertInt32ValueToDouble(val);}else{masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");}masm.bind(&noConversion);// Call the type update IC. After this everything must be infallible as we// don't save all registers here.LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(index);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch,saveRegs))returnfalse;// Reload obj->elements as callTypeUpdateIC used the scratch register.masm.loadPtr(Address(obj,NativeObject::offsetOfElements()),scratch);LabeldoStore;if(handleAdd){// If index == initLength, increment initLength.LabelinBounds;masm.branch32(Assembler::NotEqual,initLength,index,&inBounds);// Increment initLength.masm.add32(Imm32(1),initLength);// If length is now <= index, increment length too.LabelskipIncrementLength;Addresslength(scratch,ObjectElements::offsetOfLength());masm.branch32(Assembler::Above,length,index,&skipIncrementLength);masm.add32(Imm32(1),length);masm.bind(&skipIncrementLength);// Skip EmitPreBarrier as the memory is uninitialized.masm.jump(&doStore);masm.bind(&inBounds);}EmitPreBarrier(masm,element,MIRType::Value);masm.bind(&doStore);masm.storeValue(val,element);emitPostBarrierElement(obj,val,scratch,index);returntrue;}boolBaselineCacheIRCompiler::emitStoreTypedElement(){Registerobj=allocator.useRegister(masm,reader.objOperandId());Registerindex=allocator.useRegister(masm,reader.int32OperandId());ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());TypedThingLayoutlayout=reader.typedThingLayout();Scalar::Typetype=reader.scalarType();boolhandleOOB=reader.readBool();AutoScratchRegisterscratch1(allocator,masm);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Bounds check.Labeldone;LoadTypedThingLength(masm,layout,obj,scratch1);masm.branch32(Assembler::BelowOrEqual,scratch1,index,handleOOB?&done:failure->label());// Load the elements vector.LoadTypedThingData(masm,layout,obj,scratch1);BaseIndexdest(scratch1,index,ScaleFromElemWidth(Scalar::byteSize(type)));// Use ICStubReg as second scratch register. TODO: consider doing the RHS// type check/conversion as a separate IR instruction so we can simplify// this.Registerscratch2=ICStubReg;masm.push(scratch2);Labelfail;StoreToTypedArray(cx_,masm,type,val,dest,scratch2,&fail);masm.pop(scratch2);masm.jump(&done);masm.bind(&fail);masm.pop(scratch2);masm.jump(failure->label());masm.bind(&done);returntrue;}boolBaselineCacheIRCompiler::emitStoreUnboxedArrayElement(){ObjOperandIdobjId=reader.objOperandId();Int32OperandIdindexId=reader.int32OperandId();// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);JSValueTypeelementType=reader.valueType();Registerobj=allocator.useRegister(masm,objId);Registerindex=allocator.useRegister(masm,indexId);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Bounds check.AddressinitLength(obj,UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());masm.load32(initLength,scratch);masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask),scratch);masm.branch32(Assembler::BelowOrEqual,scratch,index,failure->label());// Call the type update IC. After this everything must be infallible as we// don't save all registers here.if(elementType==JSVAL_TYPE_OBJECT){LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(index);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch,saveRegs))returnfalse;}// Load obj->elements.masm.loadPtr(Address(obj,UnboxedArrayObject::offsetOfElements()),scratch);// Note that the storeUnboxedProperty call here is infallible, as the// IR emitter is responsible for guarding on |val|'s type.BaseIndexelement(scratch,index,ScaleFromElemWidth(UnboxedTypeSize(elementType)));EmitICUnboxedPreBarrier(masm,element,elementType);masm.storeUnboxedProperty(element,elementType,ConstantOrRegister(TypedOrValueRegister(val)),/* failure = */nullptr);if(UnboxedTypeNeedsPostBarrier(elementType))emitPostBarrierSlot(obj,val,scratch);returntrue;}boolBaselineCacheIRCompiler::emitStoreUnboxedArrayElementHole(){ObjOperandIdobjId=reader.objOperandId();Int32OperandIdindexId=reader.int32OperandId();// Allocate the fixed registers first. These need to be fixed for// callTypeUpdateIC.AutoScratchRegisterscratch(allocator,masm,R1.scratchReg());ValueOperandval=allocator.useFixedValueRegister(masm,reader.valOperandId(),R0);JSValueTypeelementType=reader.valueType();Registerobj=allocator.useRegister(masm,objId);Registerindex=allocator.useRegister(masm,indexId);FailurePath*failure;if(!addFailurePath(&failure))returnfalse;// Check index <= initLength.AddressinitLength(obj,UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());masm.load32(initLength,scratch);masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask),scratch);masm.branch32(Assembler::Below,scratch,index,failure->label());// Check capacity.masm.checkUnboxedArrayCapacity(obj,RegisterOrInt32Constant(index),scratch,failure->label());// Call the type update IC. After this everything must be infallible as we// don't save all registers here.if(elementType==JSVAL_TYPE_OBJECT){LiveGeneralRegisterSetsaveRegs;saveRegs.add(obj);saveRegs.add(index);saveRegs.add(val);if(!callTypeUpdateIC(obj,val,scratch,saveRegs))returnfalse;}// Load obj->elements.masm.loadPtr(Address(obj,UnboxedArrayObject::offsetOfElements()),scratch);// If index == initLength, increment initialized length.LabelinBounds,doStore;masm.load32(initLength,scratch);masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask),scratch);masm.branch32(Assembler::NotEqual,scratch,index,&inBounds);masm.add32(Imm32(1),initLength);// If length is now <= index, increment length.Addresslength(obj,UnboxedArrayObject::offsetOfLength());LabelskipIncrementLength;masm.branch32(Assembler::Above,length,index,&skipIncrementLength);masm.add32(Imm32(1),length);masm.bind(&skipIncrementLength);// Skip EmitICUnboxedPreBarrier as the memory is uninitialized.masm.jump(&doStore);masm.bind(&inBounds);BaseIndexelement(scratch,index,ScaleFromElemWidth(UnboxedTypeSize(elementType)));EmitICUnboxedPreBarrier(masm,element,elementType);// Note that the storeUnboxedProperty call here is infallible, as the// IR emitter is responsible for guarding on |val|'s type.masm.bind(&doStore);masm.storeUnboxedProperty(element,elementType,ConstantOrRegister(TypedOrValueRegister(val)),/* failure = */nullptr);if(UnboxedTypeNeedsPostBarrier(elementType))emitPostBarrierSlot(obj,val,scratch);returntrue;}typedefbool(*CallNativeSetterFn)(JSContext*,HandleFunction,HandleObject,HandleValue);staticconstVMFunctionCallNativeSetterInfo=FunctionInfo<CallNativeSetterFn>(CallNativeSetter,"CallNativeSetter");boolBaselineCacheIRCompiler::emitCallNativeSetter(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddresssetterAddr(stubAddress(reader.stubOffset()));ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Load the callee in the scratch register.masm.loadPtr(setterAddr,scratch);masm.Push(val);masm.Push(obj);masm.Push(scratch);if(!callVM(masm,CallNativeSetterInfo))returnfalse;stubFrame.leave(masm);returntrue;}boolBaselineCacheIRCompiler::emitCallScriptedSetter(){AutoScratchRegisterExcludingscratch1(allocator,masm,ArgumentsRectifierReg);AutoScratchRegisterscratch2(allocator,masm);Registerobj=allocator.useRegister(masm,reader.objOperandId());AddresssetterAddr(stubAddress(reader.stubOffset()));ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());// First, ensure our setter is non-lazy and has JIT code. This also loads// the callee in scratch1.{FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.loadPtr(setterAddr,scratch1);masm.branchIfFunctionHasNoScript(scratch1,failure->label());masm.loadPtr(Address(scratch1,JSFunction::offsetOfNativeOrScript()),scratch2);masm.loadBaselineOrIonRaw(scratch2,scratch2,failure->label());}allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch2);// Align the stack such that the JitFrameLayout is aligned on// JitStackAlignment.masm.alignJitStackBasedOnNArgs(1);// Setter is called with 1 argument, and |obj| as thisv. Note that we use// Push, not push, so that callJit will align the stack properly on ARM.masm.Push(val);masm.Push(TypedOrValueRegister(MIRType::Object,AnyRegister(obj)));// Now that the object register is no longer needed, use it as second// scratch.EmitBaselineCreateStubFrameDescriptor(masm,scratch2,JitFrameLayout::Size());masm.Push(Imm32(1));// ActualArgc// Push callee.masm.Push(scratch1);// Push frame descriptor.masm.Push(scratch2);// Load callee->nargs in scratch2 and the JIT code in scratch.LabelnoUnderflow;masm.load16ZeroExtend(Address(scratch1,JSFunction::offsetOfNargs()),scratch2);masm.loadPtr(Address(scratch1,JSFunction::offsetOfNativeOrScript()),scratch1);masm.loadBaselineOrIonRaw(scratch1,scratch1,nullptr);// Handle arguments underflow.masm.branch32(Assembler::BelowOrEqual,scratch2,Imm32(1),&noUnderflow);{// Call the arguments rectifier.MOZ_ASSERT(ArgumentsRectifierReg!=scratch1);JitCode*argumentsRectifier=cx_->runtime()->jitRuntime()->getArgumentsRectifier();masm.movePtr(ImmGCPtr(argumentsRectifier),scratch1);masm.loadPtr(Address(scratch1,JitCode::offsetOfCode()),scratch1);masm.movePtr(ImmWord(1),ArgumentsRectifierReg);}masm.bind(&noUnderflow);masm.callJit(scratch1);stubFrame.leave(masm,true);returntrue;}typedefbool(*SetArrayLengthFn)(JSContext*,HandleObject,HandleValue,bool);staticconstVMFunctionSetArrayLengthInfo=FunctionInfo<SetArrayLengthFn>(SetArrayLength,"SetArrayLength");boolBaselineCacheIRCompiler::emitCallSetArrayLength(){Registerobj=allocator.useRegister(masm,reader.objOperandId());boolstrict=reader.readBool();ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);masm.Push(Imm32(strict));masm.Push(val);masm.Push(obj);if(!callVM(masm,SetArrayLengthInfo))returnfalse;stubFrame.leave(masm);returntrue;}typedefbool(*ProxySetPropertyFn)(JSContext*,HandleObject,HandleId,HandleValue,bool);staticconstVMFunctionProxySetPropertyInfo=FunctionInfo<ProxySetPropertyFn>(ProxySetProperty,"ProxySetProperty");boolBaselineCacheIRCompiler::emitCallProxySet(){Registerobj=allocator.useRegister(masm,reader.objOperandId());ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());AddressidAddr(stubAddress(reader.stubOffset()));boolstrict=reader.readBool();AutoScratchRegisterscratch(allocator,masm);allocator.discardStack(masm);AutoStubFramestubFrame(*this);stubFrame.enter(masm,scratch);// Load the jsid in the scratch register.masm.loadPtr(idAddr,scratch);masm.Push(Imm32(strict));masm.Push(val);masm.Push(scratch);masm.Push(obj);if(!callVM(masm,ProxySetPropertyInfo))returnfalse;stubFrame.leave(masm);returntrue;}typedefbool(*ProxySetPropertyByValueFn)(JSContext*,HandleObject,HandleValue,HandleValue,bool);staticconstVMFunctionProxySetPropertyByValueInfo=FunctionInfo<ProxySetPropertyByValueFn>(ProxySetPropertyByValue,"ProxySetPropertyByValue");boolBaselineCacheIRCompiler::emitCallProxySetByValue(){Registerobj=allocator.useRegister(masm,reader.objOperandId());ValueOperandidVal=allocator.useValueRegister(masm,reader.valOperandId());ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());boolstrict=reader.readBool();allocator.discardStack(masm);// We need a scratch register but we don't have any registers available on// x86, so temporarily store |obj| in the frame's scratch slot.intscratchOffset=BaselineFrame::reverseOffsetOfScratchValue();masm.storePtr(obj,Address(BaselineFrameReg,scratchOffset));AutoStubFramestubFrame(*this);stubFrame.enter(masm,obj);// Restore |obj|. Because we entered a stub frame we first have to load// the original frame pointer.masm.loadPtr(Address(BaselineFrameReg,0),obj);masm.loadPtr(Address(obj,scratchOffset),obj);masm.Push(Imm32(strict));masm.Push(val);masm.Push(idVal);masm.Push(obj);if(!callVM(masm,ProxySetPropertyByValueInfo))returnfalse;stubFrame.leave(masm);returntrue;}boolBaselineCacheIRCompiler::emitTypeMonitorResult(){allocator.discardStack(masm);EmitEnterTypeMonitorIC(masm);returntrue;}boolBaselineCacheIRCompiler::emitReturnFromIC(){allocator.discardStack(masm);EmitReturnFromIC(masm);returntrue;}boolBaselineCacheIRCompiler::emitLoadObject(){Registerreg=allocator.defineRegister(masm,reader.objOperandId());masm.loadPtr(stubAddress(reader.stubOffset()),reg);returntrue;}boolBaselineCacheIRCompiler::emitLoadStackValue(){ValueOperandval=allocator.defineValueRegister(masm,reader.valOperandId());Addressaddr=allocator.addressOf(masm,BaselineFrameSlot(reader.uint32Immediate()));masm.loadValue(addr,val);returntrue;}boolBaselineCacheIRCompiler::emitGuardDOMExpandoMissingOrGuardShape(){ValueOperandval=allocator.useValueRegister(masm,reader.valOperandId());AutoScratchRegistershapeScratch(allocator,masm);AutoScratchRegisterobjScratch(allocator,masm);AddressshapeAddr(stubAddress(reader.stubOffset()));FailurePath*failure;if(!addFailurePath(&failure))returnfalse;Labeldone;masm.branchTestUndefined(Assembler::Equal,val,&done);masm.debugAssertIsObject(val);masm.loadPtr(shapeAddr,shapeScratch);masm.unboxObject(val,objScratch);masm.branchTestObjShape(Assembler::NotEqual,objScratch,shapeScratch,failure->label());masm.bind(&done);returntrue;}boolBaselineCacheIRCompiler::emitLoadDOMExpandoValueGuardGeneration(){Registerobj=allocator.useRegister(masm,reader.objOperandId());AddressexpandoAndGenerationAddr(stubAddress(reader.stubOffset()));AddressgenerationAddr(stubAddress(reader.stubOffset()));AutoScratchRegisterscratch(allocator,masm);ValueOperandoutput=allocator.defineValueRegister(masm,reader.valOperandId());FailurePath*failure;if(!addFailurePath(&failure))returnfalse;masm.loadPtr(Address(obj,ProxyObject::offsetOfReservedSlots()),scratch);AddressexpandoAddr(scratch,detail::ProxyReservedSlots::offsetOfPrivateSlot());// Load the ExpandoAndGeneration* in the output scratch register and guard// it matches the proxy's ExpandoAndGeneration.masm.loadPtr(expandoAndGenerationAddr,output.scratchReg());masm.branchPrivatePtr(Assembler::NotEqual,expandoAddr,output.scratchReg(),failure->label());// Guard expandoAndGeneration->generation matches the expected generation.masm.branch64(Assembler::NotEqual,Address(output.scratchReg(),ExpandoAndGeneration::offsetOfGeneration()),generationAddr,scratch,failure->label());// Load expandoAndGeneration->expando into the output Value register.masm.loadValue(Address(output.scratchReg(),ExpandoAndGeneration::offsetOfExpando()),output);returntrue;}boolBaselineCacheIRCompiler::init(CacheKindkind){if(!allocator.init())returnfalse;// Baseline ICs monitor values when needed, so returning doubles is fine.allowDoubleResult_.emplace(true);size_tnumInputs=writer_.numInputOperands();// Baseline passes the first 2 inputs in R0/R1, other Values are stored on// the stack.size_tnumInputsInRegs=std::min(numInputs,size_t(2));AllocatableGeneralRegisterSetavailable(ICStubCompiler::availableGeneralRegs(numInputsInRegs));switch(kind){caseCacheKind::GetProp:caseCacheKind::TypeOf:MOZ_ASSERT(numInputs==1);allocator.initInputLocation(0,R0);break;caseCacheKind::GetElem:caseCacheKind::GetPropSuper:caseCacheKind::SetProp:caseCacheKind::In:caseCacheKind::HasOwn:MOZ_ASSERT(numInputs==2);allocator.initInputLocation(0,R0);allocator.initInputLocation(1,R1);break;caseCacheKind::GetElemSuper:caseCacheKind::SetElem:MOZ_ASSERT(numInputs==3);allocator.initInputLocation(0,R0);allocator.initInputLocation(1,R1);allocator.initInputLocation(2,BaselineFrameSlot(0));break;caseCacheKind::GetName:caseCacheKind::BindName:MOZ_ASSERT(numInputs==1);allocator.initInputLocation(0,R0.scratchReg(),JSVAL_TYPE_OBJECT);#if defined(JS_NUNBOX32)// availableGeneralRegs can't know that GetName/BindName is only using// the payloadReg and not typeReg on x86.available.add(R0.typeReg());#endifbreak;caseCacheKind::Call:MOZ_ASSERT(numInputs==1);allocator.initInputLocation(0,R0.scratchReg(),JSVAL_TYPE_INT32);#if defined(JS_NUNBOX32)// availableGeneralRegs can't know that Call is only using// the payloadReg and not typeReg on x86.available.add(R0.typeReg());#endifbreak;}// Baseline doesn't allocate float registers so none of them are live.liveFloatRegs_=LiveFloatRegisterSet(FloatRegisterSet());allocator.initAvailableRegs(available);outputUnchecked_.emplace(R0);returntrue;}staticconstsize_tMaxOptimizedCacheIRStubs=16;ICStub*jit::AttachBaselineCacheIRStub(JSContext*cx,constCacheIRWriter&writer,CacheKindkind,ICStubEngineengine,JSScript*outerScript,ICFallbackStub*stub,bool*attached){// We shouldn't GC or report OOM (or any other exception) here.AutoAssertNoPendingExceptionaanpe(cx);JS::AutoCheckCannotGCnogc;MOZ_ASSERT(!*attached);if(writer.failed())returnnullptr;// Just a sanity check: the caller should ensure we don't attach an// unlimited number of stubs.MOZ_ASSERT(stub->numOptimizedStubs()<MaxOptimizedCacheIRStubs);enumclassCacheIRStubKind{Regular,Monitored,Updated};uint32_tstubDataOffset;CacheIRStubKindstubKind;switch(kind){caseCacheKind::In:caseCacheKind::HasOwn:caseCacheKind::BindName:caseCacheKind::TypeOf:stubDataOffset=sizeof(ICCacheIR_Regular);stubKind=CacheIRStubKind::Regular;break;caseCacheKind::GetProp:caseCacheKind::GetElem:caseCacheKind::GetName:caseCacheKind::GetPropSuper:caseCacheKind::GetElemSuper:caseCacheKind::Call:stubDataOffset=sizeof(ICCacheIR_Monitored);stubKind=CacheIRStubKind::Monitored;break;caseCacheKind::SetProp:caseCacheKind::SetElem:stubDataOffset=sizeof(ICCacheIR_Updated);stubKind=CacheIRStubKind::Updated;break;}JitZone*jitZone=cx->zone()->jitZone();// Check if we already have JitCode for this stub.CacheIRStubInfo*stubInfo;CacheIRStubKey::Lookuplookup(kind,engine,writer.codeStart(),writer.codeLength());JitCode*code=jitZone->getBaselineCacheIRStubCode(lookup,&stubInfo);if(!code){// We have to generate stub code.JitContextjctx(cx,nullptr);BaselineCacheIRCompilercomp(cx,writer,engine,stubDataOffset);if(!comp.init(kind))returnnullptr;code=comp.compile();if(!code)returnnullptr;// Allocate the shared CacheIRStubInfo. Note that the// putBaselineCacheIRStubCode call below will transfer ownership// to the stub code HashMap, so we don't have to worry about freeing// it below.MOZ_ASSERT(!stubInfo);stubInfo=CacheIRStubInfo::New(kind,engine,comp.makesGCCalls(),stubDataOffset,writer);if(!stubInfo)returnnullptr;CacheIRStubKeykey(stubInfo);if(!jitZone->putBaselineCacheIRStubCode(lookup,key,code))returnnullptr;}MOZ_ASSERT(code);MOZ_ASSERT(stubInfo);MOZ_ASSERT(stubInfo->stubDataSize()==writer.stubDataSize());// Ensure we don't attach duplicate stubs. This can happen if a stub failed// for some reason and the IR generator doesn't check for exactly the same// conditions.for(ICStubConstIteratoriter=stub->beginChainConst();!iter.atEnd();iter++){boolupdated=false;switch(stubKind){caseCacheIRStubKind::Regular:{if(!iter->isCacheIR_Regular())continue;autootherStub=iter->toCacheIR_Regular();if(otherStub->stubInfo()!=stubInfo)continue;if(!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(),&updated))continue;break;}caseCacheIRStubKind::Monitored:{if(!iter->isCacheIR_Monitored())continue;autootherStub=iter->toCacheIR_Monitored();if(otherStub->stubInfo()!=stubInfo)continue;if(!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(),&updated))continue;break;}caseCacheIRStubKind::Updated:{if(!iter->isCacheIR_Updated())continue;autootherStub=iter->toCacheIR_Updated();if(otherStub->stubInfo()!=stubInfo)continue;if(!writer.stubDataEqualsMaybeUpdate(otherStub->stubDataStart(),&updated))continue;break;}}// We found a stub that's exactly the same as the stub we're about to// attach. Just return nullptr, the caller should do nothing in this// case.if(updated)*attached=true;returnnullptr;}// Time to allocate and attach a new stub.size_tbytesNeeded=stubInfo->stubDataOffset()+stubInfo->stubDataSize();ICStubSpace*stubSpace=ICStubCompiler::StubSpaceForStub(stubInfo->makesGCCalls(),outerScript,engine);void*newStubMem=stubSpace->alloc(bytesNeeded);if(!newStubMem)returnnullptr;switch(stubKind){caseCacheIRStubKind::Regular:{autonewStub=new(newStubMem)ICCacheIR_Regular(code,stubInfo);writer.copyStubData(newStub->stubDataStart());stub->addNewStub(newStub);*attached=true;returnnewStub;}caseCacheIRStubKind::Monitored:{ICStub*monitorStub=stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();autonewStub=new(newStubMem)ICCacheIR_Monitored(code,monitorStub,stubInfo);writer.copyStubData(newStub->stubDataStart());stub->addNewStub(newStub);*attached=true;returnnewStub;}caseCacheIRStubKind::Updated:{autonewStub=new(newStubMem)ICCacheIR_Updated(code,stubInfo);if(!newStub->initUpdatingChain(cx,stubSpace)){cx->recoverFromOutOfMemory();returnnullptr;}writer.copyStubData(newStub->stubDataStart());stub->addNewStub(newStub);*attached=true;returnnewStub;}}MOZ_CRASH("Invalid kind");}uint8_t*ICCacheIR_Regular::stubDataStart(){returnreinterpret_cast<uint8_t*>(this)+stubInfo_->stubDataOffset();}uint8_t*ICCacheIR_Monitored::stubDataStart(){returnreinterpret_cast<uint8_t*>(this)+stubInfo_->stubDataOffset();}uint8_t*ICCacheIR_Updated::stubDataStart(){returnreinterpret_cast<uint8_t*>(this)+stubInfo_->stubDataOffset();}/* static */ICCacheIR_Monitored*ICCacheIR_Monitored::Clone(JSContext*cx,ICStubSpace*space,ICStub*firstMonitorStub,ICCacheIR_Monitored&other){constCacheIRStubInfo*stubInfo=other.stubInfo();MOZ_ASSERT(stubInfo->makesGCCalls());size_tbytesNeeded=stubInfo->stubDataOffset()+stubInfo->stubDataSize();void*newStub=space->alloc(bytesNeeded);if(!newStub)returnnullptr;ICCacheIR_Monitored*res=new(newStub)ICCacheIR_Monitored(other.jitCode(),firstMonitorStub,stubInfo);stubInfo->copyStubData(&other,res);returnres;}/* static */ICCacheIR_Updated*ICCacheIR_Updated::Clone(JSContext*cx,ICStubSpace*space,ICStub*firstMonitorStub,ICCacheIR_Updated&other){constCacheIRStubInfo*stubInfo=other.stubInfo();MOZ_ASSERT(stubInfo->makesGCCalls());size_tbytesNeeded=stubInfo->stubDataOffset()+stubInfo->stubDataSize();void*newStub=space->alloc(bytesNeeded);if(!newStub)returnnullptr;ICCacheIR_Updated*res=new(newStub)ICCacheIR_Updated(other.jitCode(),stubInfo);res->updateStubGroup()=other.updateStubGroup();res->updateStubId()=other.updateStubId();stubInfo->copyStubData(&other,res);returnres;}